// virtio 磁盘驱动
// 参考 xv6-riscv 实现，简化版本

#include "types.h"
#include "riscv.h"
#include "defs.h"
#include "param.h"
#include "memlayout.h"
#include "spinlock.h"
#include "fs/buf.h"
#include "fs/virtio.h"

// virtio mmio 寄存器访问
#define R(r) ((volatile uint32 *)(VIRTIO0 + (r)))

static struct disk {
  // DMA 描述符集合
  struct virtq_desc *desc;
  
  // 可用环（驱动写入）
  struct virtq_avail *avail;
  
  // 已用环（设备写入）
  struct virtq_used *used;
  
  // 自己的簿记
  char free[NUM];  // 描述符是否空闲
  uint16 used_idx; // 已查看的已用环索引
  
  // 跟踪进行中的操作信息
  struct {
    struct buf *b;
    char status;
  } info[NUM];
  
  // 磁盘命令头
  struct virtio_blk_req ops[NUM];
  
  struct spinlock vdisk_lock;
} disk;

static int disk_initialized = 0;

int
virtio_disk_init(void)
{
  uint32 status = 0;
  
  initlock(&disk.vdisk_lock, "virtio_disk");
  
  // 检查 virtio 魔数
  uint32 magic = *R(VIRTIO_MAGIC);
  uint32 version = *R(VIRTIO_VERSION);
  uint32 device_id = *R(VIRTIO_DEVICE_ID);
  
  // 如果设备不存在（读取返回0或全1），返回错误
  if(magic == 0 || magic == 0xFFFFFFFF ||
     version == 0 || version == 0xFFFFFFFF ||
     device_id == 0 || device_id == 0xFFFFFFFF) {
    printf("virtio_disk_init: virtio device not found\n");
    return -1;
  }
  
  if(magic != VIRTIO_MAGIC_VALUE ||
     version != VIRTIO_VERSION_LEGACY ||
     device_id != VIRTIO_DEVICE_ID_BLOCK) {
    printf("virtio_disk_init: magic=0x%x version=0x%x device_id=0x%x\n", 
           magic, version, device_id);
    return -1;
  }
  
  // 重置设备
  *R(VIRTIO_STATUS) = status;
  
  // 设置 ACK 位
  status |= VIRTIO_STATUS_ACK;
  *R(VIRTIO_STATUS) = status;
  
  // 设置 DRIVER 位
  status |= VIRTIO_STATUS_DRIVER;
  *R(VIRTIO_STATUS) = status;
  
  // 协商特性（简化：接受所有特性）
  uint64 features = *R(VIRTIO_DEVICE_FEATURES);
  *R(VIRTIO_DRIVER_FEATURES) = features;
  status |= VIRTIO_STATUS_FEATURES_OK;
  *R(VIRTIO_STATUS) = status;
  
  // 检查特性协商是否成功
  if(!(*R(VIRTIO_STATUS) & VIRTIO_STATUS_FEATURES_OK)) {
    printf("virtio_disk_init: FEATURES_OK failed\n");
    return -1;
  }
  
  // 初始化队列 0
  *R(VIRTIO_QUEUE_SEL) = 0;
  uint32 max = *R(VIRTIO_QUEUE_NUM_MAX);
  if(max == 0) {
    printf("virtio_disk_init: has no queue 0\n");
    return -1;
  }
  if(max < NUM) {
    printf("virtio_disk_init: max queue too short (max=%d, need=%d)\n", max, NUM);
    return -1;
  }
  *R(VIRTIO_QUEUE_NUM) = NUM;
  
  // 分配并初始化描述符、可用环、已用环
  // 简化：使用静态分配的内存
  // 注意：实际实现需要确保这些内存是物理连续的
  // 这里先提供框架，后续需要完善内存分配
  
  // 设置队列就绪
  *R(VIRTIO_QUEUE_READY) = 0x1;
  
  // 设置状态为 DRIVER_OK
  status |= VIRTIO_STATUS_DRIVER_OK;
  *R(VIRTIO_STATUS) = status;
  
  // 初始化描述符空闲标记
  for(int i = 0; i < NUM; i++)
    disk.free[i] = 1;
  
  printf("virtio_disk_init: initialized successfully\n");
  disk_initialized = 1;
  return 0;
}

// 读取或写入一个块
void
virtio_disk_rw(struct buf *b, int write)
{
  if(!disk_initialized) {
    panic("virtio_disk_rw: disk not initialized");
  }
  
  uint64 sector = b->blockno * (BSIZE / 512);
  
  acquire(&disk.vdisk_lock);
  
  // 等待一个空闲描述符
  int idx = 0;
  for(int i = 0; i < NUM; i++) {
    if(disk.free[i]) {
      idx = i;
      break;
    }
  }
  if(idx == 0 && !disk.free[0])
    panic("virtio_disk_rw: no free descriptor");
  
  disk.free[idx] = 0;
  disk.info[idx].b = b;
  disk.info[idx].status = 0;
  
  // 设置请求
  struct virtio_blk_req *req = &disk.ops[idx];
  req->type = write ? VIRTIO_BLK_T_OUT : VIRTIO_BLK_T_IN;
  req->reserved = 0;
  req->sector = sector;
  
  // 简化实现：这里需要设置描述符链
  // 实际实现需要：
  // 1. 设置描述符指向请求头
  // 2. 设置描述符指向数据缓冲区
  // 3. 设置描述符指向状态字节
  // 4. 将描述符索引添加到可用环
  // 5. 通知设备
  
  // TODO: 完善描述符链设置和通知机制
  
  // 等待完成
  while(disk.info[idx].status == 0) {
    // 检查中断状态
    // 简化：轮询方式（实际应该使用中断）
  }
  
  disk.free[idx] = 1;
  release(&disk.vdisk_lock);
}